# 导入类
# 随着不断给类添加功能，文件可能变得很长，即便妥善地使用了继
# 承亦如此。为遵循Python的总体理念，应让文件尽可能整洁。
# Python在这方面提供了帮助，允许将类存储在模块中，然后在主程
# 序中导入所需的模块。

# 导入单个类
# 下面来创建一个只包含Car 类的模块。这让我们面临一个微妙的命
# 名问题：在本章中已经有一个名为car.py的文件，但这个模块也应
# 命名为car.py，因为它包含表示汽车的代码。我们将这样解决这个
# 命名问题：将Car 类存储在一个名为car.py的模块中，该模块将覆
# 盖前面使用的文件car.py。从现在开始，使用该模块的程序都必须
# 使用更具体的文件名，如my_car.py。下面是模块car.py，其中只包
# 含Car 类的代码：
# car.py

"""一个可用于表示汽车的类"""
class Car:
	"""一次模拟汽车的简单尝试"""			# 模块级文档字符串，对该模块的内容做了简要的描述。
										# 应为自己创建的每个模块编写文档字符串。

	def __init__(self, make, model, year):
		"""初始化描述汽车的属性"""
		self.make = make
		self.model = model
		self.year = year
		self.odometer_reading = 0

	def get_descriptive_name(self):
		"""返回整洁的描述性名称"""
		long_name = f"{self.year} {self.make} {self.model}"
		return long_name.title()

	def read_odometer(self):
		"""打印一条消息，指出汽车的里程"""
		print(f"This car has {self.odometer_reading} miles on it.")

	def update_odometer(self, mileage):
	 	"""
		将里程表读书设置为指定的值
		拒绝将里程表往回调
	 	"""
	 	if mileage >= self.odometer_reading:
	 		self.odometer_reading = mileage
	 	else:
	 		print("You can't roll back an odometer!")

	def increment_odometer(self, miles):
	 	"""将里程表读数增加指定的量"""
	 	self.odometer_reading += miles


# 下面来创建另一个文件my_car.py，在其中导入Car 类并创建其实例：
# my_car.py
# from car import Car 			# import 语句让Python打开模块car 并导入其中的Car 类。
								# 这样，我们就可以使用Car 类，就像它是在这个文件中定义的一样。

# my_new_car = Car('audi', 'a4', 2019)
# print(my_new_car,.get_descriptive_name())

# my_new_car.odometer_reading = 23
# my_new_car.read_odometer()

# 导入类是一种有效的编程方式。如果这个程序包含整个Class 类，
# 它该有多长啊！通过将这个类移到一个模块中并导入该模块，依然
# 可以使用其所有功能，但主程序文件变得整洁而易于阅读了。这还
# 让你能够将大部分逻辑存储在独立的文件中。确定类像你希望的那
# 样工作后，就可以不管这些文件，而专注于主程序的高级逻辑了


# 在一个模块中存储多个类
# 虽然同一个模块中的类之间应存在某种相关性，但可根据需要在一
# 个模块中存储任意数量的类。Battery 类和ElectricCar 类都可
# 帮助模拟汽车，下面将它们都加入模块car.py中：

"""一组用于表示燃油汽车和电动汽车的类。"""
class Car:
	"""一次模拟汽车的简单尝试"""

	def __init__(self, make, model, year):
		self.make = make
		self.model = model
		self.year = year
		self.odometer_reading = 0

	def get_descriptive_name(self):
		long_name = f"{self.year} {self.make} {self.model}"
		return long_name.title()

	def read_odometer(self):
		print(f"This car has {self.odometer_reading} miles on it.")

	def update_odometer(self, mileage):
		if mileage >= self.odometer_reading:
			self.odometer_reading = mileage
		else:
			print("You can't roll back an odometer!")

	def increment_odometer(self, miles):
		self.odometer_reading += miles

	def fill_gas_tank(self):
		"""提示汽车油箱需要加油"""
		print("Please fill your car gas tank!")

class Battery:
	"""一次模拟电动汽车电瓶的简单尝试。"""
	
	def __init__(self, battery_size=75):
		"""初始化电瓶的属性。"""
		self.battery_size = battery_size
	
	def describe_battery(self):
		"""打印一条描述电瓶容量的消息。"""
		print(f"This car has a {self.battery_size}-kWh battery.")

	def get_range(self):
		"""打印一条描述电瓶续航里程的消息。"""
		if self.battery_size == 75:
			range = 260
		elif self.battery_size == 100:
			range = 315
		print(f"This car can go about {range} miles on a full charge.")

class ElectricCar(Car):
	"""模拟电动汽车的独特之处。"""
	def __init__(self, make, model, year):
		"""
		初始化父类的属性。
		再初始化电动汽车特有的属性。
		"""
		super().__init__(make, model, year)
		self.battery = Battery()

# 现在，可以新建一个名为my_electric_car.py的文件，导入ElectricCar 类，并创建一辆电动汽车了：

# my_electric_car.py

# from car import ElectricCar
# my_tesla = ElectricCar('tesla', 'model s', 2019)
# print(my_tesla.get_descriptive_name())
# my_tesla.battery.describe_battery()
# my_tesla.battery.get_range()



# 　从一个模块中导入多个类
# 可根据需要在程序文件中导入任意数量的类。如果要在同一个程序
# 中创建普通汽车和电动汽车，就需要将Car 类和ElectricCar 类都导入：
# my_cars.py
from car import Car, ElectricCar				# 从一个模块中导入多个类时，用逗号分隔了各个类。
												# 导入必要的类后，就可根据需要创建每个类的任意数量实例。
my_beetle = Car('volkswagen', 'beetle', 2019)
print(my_beetle.get_descriptive_name())
my_tesla = ElectricCar('tesla', 'roadster', 2019)
print(my_tesla.get_descriptive_name())



# 导入整个模块
# 还可以导入整个模块，再使用句点表示法访问需要的类。这种导入
# 方式很简单，代码也易于阅读。因为创建类实例的代码都包含模块
# 名，所以不会与当前文件使用的任何名称发生冲突。
# 下面的代码导入整个car 模块，并创建一辆普通汽车和一辆电动汽车：
# my_cars.py
import car  				# 导入了整个car 模块。接下来，使用语法module_name.ClassName 访问需要的类。
my_beetle = Car('volkswagen', 'beetle', 2019)
print(my_beetle.get_descriptive_name())
my_tesla = ElectricCar('tesla', 'roadster', 2019)
print(my_tesla.get_descriptive_name())



# 导入模块中的所有类
# 要导入模块中的每个类，可使用下面的语法：
# from module_name import *
# 不推荐使用这种导入方式，原因有二。第一，如果只看文件开头的
# import 语句，就能清楚地知道程序使用了哪些类，将大有裨益。
# 然而这种导入方式没有明确地指出使用了模块中的哪些类。第二，
# 这种方式还可能引发名称方面的迷惑。如果不小心导入了一个与程
# 序文件中其他东西同名的类，将引发难以诊断的错误。这里之所以
# 介绍这种导入方式，是因为虽然不推荐使用，但你可能在别人编写
# 的代码中见到它。
# 需要从一个模块中导入很多类时，最好导入整个模块，并使用
# module_name.ClassName 语法来访问类。这样做时，虽然文件开
# 头并没有列出用到的所有类，但你清楚地知道在程序的哪些地方使
# 用了导入的模块。这也避免了导入模块中的每个类可能引发的名称
# 冲突。



# 在一个模块中导入另一个模块
# 有时候，需要将类分散到多个模块中，以免模块太大或在同一个模
# 块中存储不相关的类。将类存储在多个模块中时，你可能会发现一
# 个模块中的类依赖于另一个模块中的类。在这种情况下，可在前一
# 个模块中导入必要的类。
# 下面将Car 类存储在一个模块中，并将ElectricCar 类和Battery
# 类存储在另一个模块中。将第二个模块命名为electric_car.py（这将
# 覆盖前面创建的文件electric_car.py），并将Battery 类和
# ElectricCar 类复制到这个模块中：

# electric_car.py
"""一组可用于表示电动汽车的类。"""

# from car import Car 			ElectricCar 类需要访问其父类Car ，因此在此处直接将Car 类导入该模块中。
# 如果忘记了这行代码，Python将在我们试图创建ElectricCar 实例时引发错误。还需要更新模块car ，使其只包含Car 类

# class Battery:
# 	--snip--

# class ElectricCar(Car):
# 	--snip--

# car.py
"""一个可用于表示汽车的类。"""

class Car:
	--snip--

# 现在可以分别从每个模块中导入类，以根据需要创建任何类型的汽车了：
# my_cars.py

# from car import Car
# from electric_car import ElectricCar
# 从模块car 中导入了Car 类，并从模块electric_car 中导入ElectricCar 类。

# my_beetle = Car('volkswagen', 'beetle', 2019)
# print(my_beetle.get_descriptive_name())
# my_tesla = ElectricCar('tesla', 'roadster', 2019)
# print(my_tesla.get_descriptive_name())


# 使用别名
# 第8章说过，使用模块来组织项目代码时，别名大有裨益。导入类时，也可为其指定别名。
# 例如，要在程序中创建大量电动汽车实例，需要反复输入ElectricCar ，非常烦琐。
# 为避免这种烦恼，可在import 语句中给ElectricCar 指定一个别名：
from electric_car import ElectricCar as EC 
# 现在每当需要创建电动汽车实例时，都可使用这个别名：
my_tesla = EC('tesla', 'roadster', 2019)


# 自定义工作流程
# 如你所见，在组织大型项目的代码方面，Python提供了很多选项。
# 熟悉所有这些选项很重要，这样你才能确定哪种项目组织方式是最
# 佳的，并能理解别人开发的项目。
# 一开始应让代码结构尽可能简单。先尽可能在一个文件中完成所有
# 的工作，确定一切都能正确运行后，再将类移到独立的模块中。如
# 果你喜欢模块和文件的交互方式，可在项目开始时就尝试将类存储
# 到模块中。先找出让你能够编写出可行代码的方式，再尝试改进代码。